KMSで認証情報を暗号化しLambda実行時に復号化する
データベースやAPIサーバーなど外部システムと連携する際には、往々にして認証情報が必要になります。
今回は AWS Key Management Service (以下 KMS) の共通鍵暗号の仕組みを使い、暗号化した認証情報を AWS Lambda 関数のコードに埋め込み、関数呼び出し時に認証情報を復号化する方法を紹介します。
基本的なアイデアは次のブログで書かれており、KMS を使った暗号化処理だけを自分向けメモも兼ねて抜き出しました。
http://ijin.github.io/blog/2015/08/06/github-to-lambda-to-slack/
KMS ではマスターキーを使って暗号・復号する処理が API で切り出されているため、この API を使って認証情報を暗号化します。
KMS と Lambda の連携
以下の流れで動作確認します。
- AWS KMSマスターキーの作成
- AWS CLI からマスターキーを使って暗号・復号処理を確認
- AWS Lambda から暗号化されたデータ(ciphertext)を復号して出力
- AWS Lambda 関数に KMS:Decrypt 権限を与える
- AWS Lambda 関数を実行して復号されていることを確認
KMS マスターキーの作成
まずは Lambda 関数用のマスターキーを作成します。
マネージメントコンソールにログインし、「IAM→Encryption Keys」と選択すると、 KMS の管理画面に移動できます。 KMS 単独のメニューは存在しないので、ご注意ください。
KMS の鍵はリージョンごとに個別に存在します。 Filter のプルダウンで鍵を作成するリージョンを選択し、「Create Key」をクリックして、マスターキーの作成画面に移動します。
Key Administrative Permissions と Key Permissions には普段利用している管理系ユーザーを選択します。
AWS CLI から暗号・復号処理を確認
プログラムを書く前に、CLIから暗号・復号操作を確認します。
マスターキーの一覧を確認します。
$ aws kms list-keys { "Keys": [ { "KeyArn": "arn:aws:kms:ap-northeast-1:123456789012:key/xxx-yyy-zzz", "KeyId": "xxx-yyy-zzz" } ], "Truncated": false }
マスターキーのARNを環境変数に設定します。 KeyIdとエイリアスの2通りの指定があります。
$ export KEYID=arn:aws:kms:ap-northeast-1:123456789012:key/xxx-yyy-zzz # KeyId $ export KEYID=arn:aws:kms:ap-northeast-1:123456789012:alias/lambda # Alias
マスターキーを使って暗号化
マスターキーで暗号化するには Encrypt
API を使います。
$ aws kms encrypt --key-id $KEYID --plaintext 'hello, world!' { "KeyId": "arn:aws:kms:ap-northeast-1:123456789012:key/xxx-yyy-zzz", "CiphertextBlob": "CiAUkK3nep3+LDfCjRPA5NDnd5NEXv5BWWjweqEySvaTLBKUAQEBAgB4FJCt53qd/iw3wo0TwOTQ53eTRF7+QVlo8HqhMkr2kywAAABrMGkGCSqGSIb3DQEHBqBcMFoCAQAwVQYJKoZIhvcNAQcBMB4GCWCGSAFlAwQBLjARBAxHrDshpbxSGRgMAXECARCAKHfUd1sJjxRX/7tq7twil6vaXjtPZsnr9AURI1gjR+RPL4WlQTvNDjE=" }
レスポンスの CiphertextBlob
は暗号化した plaintext(つまり ciphertext) を base64 エンコードしたものです。
base64 デコードして encrypted というファイル名で ciphertext を保存します。
$ aws kms encrypt --key-id $KEYID --plaintext 'hello, world!' --query CiphertextBlob --output text | base64 --decode > encrypted
マスターキーを使って復号化
マスターキーで復号化するには Decrypt
API を使います。
$ aws kms decrypt --ciphertext-blob fileb://encrypted { "Plaintext": "aGVsbG8sIHdvcmxkIQ==", "KeyId": "arn:aws:kms:ap-northeast-1:123456789012:key/xxx-yyy-zzz" }
レスポンスの Plaintext
は復号化したデータ(つまり plaintext)を base64 エンコードしたものです。
base64 デコードすれば plaintext となります。
$ aws kms decrypt --ciphertext-blob fileb://encrypted --query Plaintext --output text | base64 --decode hello, world!
Lambda 関数化
KMS で暗号化かつ base64 エンコードした文字列を Lambda 関数に予め埋め込み、Lambda 関数実行時に復号化してみます。
通常であれば、復号化したユーザーデータを元に何か処理を行うでしょうが、今回は復号化した文字列そのままレスポンスとして返します。
Lambda 関数は Python で実装します。
import base64 import boto3 kms = boto3.client('kms') # base64 encoded ciphertext ciphertext_blob_encoded = 'CiAUkK3nep3+LDfCjRPA5NDnd5NEXv5BWWjweqEySvaTLBKUAQEBAgB4FJCt53qd/iw3wo0TwOTQ53eTRF7+QVlo8HqhMkr2kywAAABrMGkGCSqGSIb3DQEHBqBcMFoCAQAwVQYJKoZIhvcNAQcBMB4GCWCGSAFlAwQBLjARBAxHrDshpbxSGRgMAXECARCAKHfUd1sJjxRX/7tq7twil6vaXjtPZsnr9AURI1gjR+RPL4WlQTvNDjE=' # ciphertext ciphertext_blob = base64.b64decode(ciphertext_blob_encoded) def lambda_handler(event, context): dec = kms.decrypt(CiphertextBlob = ciphertext_blob) return dec['Plaintext'] #plaintext
ciphertext_blob_encoded には ciphertext を base64 エンコードした文字列を設定します。先の例で CLI から encrypt した時の文字列を使いまわします。
KMS の操作権限を Lambda 関数に与える
Lambda 関数に設定した Role に マスターキーで復号するための権限を追加します。 Role にポリシーを追加します。
{ "Version": "2012-10-17", "Statement": [ { "Sid": "Stmt1448696327000", "Effect": "Allow", "Action": [ "kms:Decrypt" ], "Resource": [ "arn:aws:kms:ap-northeast-1:123456789012:key/xxx-yyy-zzz" ] } ] }
Resource には KMS マスターキーの arn を設定します。
Lambda 関数を実行する
$ aws lambda invoke \ --function-name kms_demo \ --payload '' \ response.txt { "StatusCode": 200 } $ cat response.txt "hello, world!"
無事、復号されていますね。
まとめ
センシティブなデータは KMS で暗号化すれば、プログラムのソースコード内に気兼ねなくベタ書きできます。 ソースコードの取得と KMS の Decrypt API を実行できないかぎり、元データは復元できません。
KMS は使いみちがよくわからないという声をたまに聞きますが、地味だけと非常によくできる子です。そんな KMS のユースケースの紹介でした。